package com.youyun56.gps.schedual;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.Semaphore;

import localhost.WsResult;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;

import com.j3.sql.GridUtils;
import com.j3.sql.SqlFunc;
import com.youyun56.commons.DateUtils;
import com.youyun56.pojo.Car;

public class GPSReader implements Runnable {

    private final static Logger logger = Logger.getLogger(GPSReader.class);
    private Long start;
    private boolean back = false;
    private SocketChannel client;
    private Selector selector;
    private Semaphore mutex;
    private GPSWriter writer;

    public GPSReader(Semaphore mutex, Selector selector, SocketChannel client, GPSWriter writer) throws IOException {
        this.mutex = mutex;
        this.client = client;
        this.selector = selector;
        this.writer = writer;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
        StringBuffer sb = new StringBuffer();
        Car car = GPS.currentCars.get(client);
        logger.info(String.format("%s READER START SUCCESS %s", car.getCarMobile(), back));
        this.start = System.currentTimeMillis();
        thread: while (GPS.status == ServiceStatusType.active && !back) {
            try {
                logger.info(String.format("want %s back info", car.getCarMobile()));
                selector.select();
                Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                    iter.remove();
                    if (key.isReadable()) {
                        logger.info("Reading...");
                        SocketChannel channel = (SocketChannel) key.channel();
                        int count = channel.read(buffer);
                        logger.info("start read");
                        if (count > 0) {
                            buffer.flip();
                            String line = null;
                            while (buffer.hasRemaining()) {
                                sb.append(String.valueOf((char) buffer.get()));
                            }
                            logger.info(sb.toString());
                            // StringReader sr = new
                            // StringReader(sb.toString());
                            // BufferedReader br = new BufferedReader(sr);

                            while ((line = MessageAnalysis.line(sb)) != null) {
                                String[] infos = MessageAnalysis.infos(line);
                                String mobile = car.getCarMobile();
                                if (infos[0].equals(GPSFactory.gpsInfoHead)) {
                                    if (infos[1].indexOf(mobile) == -1) {
                                        logger.info(String.format("REQUEST %s BACK %s", mobile, infos[1]));
                                        continue;
                                    }

                                    if (infos.length > 6) {
                                        logger.info("==================================================");
                                        String x = infos[4];
                                        String y = infos[5];
                                        start = System.currentTimeMillis();
                                        URL url = new URL(GPSFactory.getGeocoding().replace("#X", y).replace("#Y", x));
                                        StringBuffer html = null;
                                        InputStreamReader isr = new InputStreamReader(url.openStream(), "utf-8");
                                        BufferedReader br = new BufferedReader(isr);
                                        String lin = null;
                                        html = new StringBuffer();
                                        while ((lin = br.readLine()) != null) {
                                            html.append(lin);
                                            logger.info(lin);
                                        }
                                        isr.close();
                                        JSONObject o = JSONObject.fromObject(html.toString());
                                        // logger.info(o);
                                        doMessage(o, html.toString(), x, y);
                                        back = true;
                                        break thread;
                                    }
                                } else if (infos[0].equals(GPSFactory.gpsSuccessHead)) {
                                    logger.info(String.format("gps success: %s", line));
                                    continue;
                                } else {
                                    logger.error(String.format("unexcept info: %s", line));
                                }
                            }
                            buffer.clear();
                        } else {
                            client.close();
                            this.selector.wakeup();
                            break thread;
                        }

                    }
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                try {
                    this.writer.setRun(false);
                    this.writer.setSelector(null);

                    client.socket().close();

                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    // e1.printStackTrace();
                }
                logger.error("IOException",e);
                back = true;
                break;
            }
        }
        try {
            if (!client.socket().isClosed()) {
                client.register(selector, SelectionKey.OP_WRITE);
            }
            mutex.release();
            logger.info("Finish read thread");
        } catch (ClosedChannelException e) {
            logger.error("ClosedChannelException",e);
        }

    }

    public void doMessage(JSONObject o, String line, String x, String y) {

        try {
            GoogleStatus status = GoogleStatus.valueOf(o.getString("status"));
            if (status == GoogleStatus.OK) {
                JSONArray array = o.getJSONArray("results");
                if (array.size() >= 3) {
                    JSONObject address = (JSONObject) array.get(0);
                    logger.info(address);
                    JSONArray addresses = address.getJSONArray("address_components");
                    int len = addresses.size();
                    StringBuffer local = new StringBuffer();
                    String city = null;
                    for (int i = len - 1; i >= 0; i--) {
                        JSONObject point = (JSONObject) addresses.get(i);
                        String longName = point.getString(GoogleAddress.long_name);
                        String type = (String) point.getJSONArray(GoogleAddress.types).get(0);
                        if (type.equals(GoogleAddress.locality)) {
                            city = longName;
                            local.append(longName);
                        } else if (type.equals(GoogleAddress.neighborhood)) {
                            local.append(longName);
                            break;
                        } else if (type.equals(GoogleAddress.postal_code) || type.equals(GoogleAddress.country)) {
                            continue;
                        } else {
                            local.append(longName);
                        }
                    }
                    logger.info(String.format("City : %s ,LOCAL : %s", city, local));
                    Car car = GPS.currentCars.get(this.client);
                    GridUtils grid = new GridUtils();
                    grid.init(GPSFactory.localDB, "select id from city where name like '%" + city + "%'");

                    if (grid.next()) {

                        WsResult result = GPSFactory.gpsService.updateCarStatus(car.getCarMobile(), grid.getInt("id"), local.toString(), x, y);
                        logger.info(result);
                    } else {
                        SqlFunc func = new SqlFunc();
                        func.executeUpdate(GPSFactory.localDB, String.format(
                                "insert into claimers(mobile,startTime,endTime,status) values('%s','%s','%s',%d)", car.getCarMobile(),
                                DateUtils.FULL_DATE_FORMAT.format(new Date(start)), DateUtils.FULL_DATE_FORMAT.format(new Date()),
                                ClaimerStatus.CITYNOTFOUND.ordinal()));
                        StringBuffer sb = new StringBuffer();
                        sb.append("Result :").append(o.toString());
                        sb.append("City : ").append(city);
                        sb.append("Local : ").append(local);
                        logger.error("City not Found: " + sb.toString());
                    }
                }
            } else if (status == GoogleStatus.ZERO_RESULTS) {
                logger.error(line = " | " + GoogleStatus.OVER_QUERY_LIMIT.name());
            } else if (status == GoogleStatus.OVER_QUERY_LIMIT) {
                logger.error(line = " | " + GoogleStatus.OVER_QUERY_LIMIT.name());
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            logger.error(e.getMessage(),e);
        }

    }

    public Long getStart() {
        return start;
    }

    public void setBack(boolean back) {
        this.back = back;
    }

    public void release() {
        this.back = true;
        logger.info("release");
        this.selector.wakeup();

        // GPS.readers

    }

    public SocketChannel getClient() {
        return client;
    }

    public void setClient(SocketChannel client) {
        this.client = client;
    }

}
